perf: reduce CPU overhead in gzip decompression and header lowercasing - #17
perf: reduce CPU overhead in gzip decompression and header lowercasing#17burruplambert wants to merge 3 commits into
Conversation
The stdlib compress/gzip and compress/flate use a slower Huffman decoder. klauspost/compress is a drop-in replacement with a faster implementation, measured at ~5% total CPU reduction in production profiling of a high-throughput HTTP client workload.
…encoding headerSorter.Less called strings.ToLower on every comparison during sort, causing O(n log n) allocations per request. Pre-compute lowercase keys once before sorting and swap them in parallel with the key-value pairs. In http2 encodeHeaders and encodeTrailers, replace strings.ToLower with lowerHeader which uses the existing common header map for zero-alloc lookups on standard headers. Combined savings: ~2% total CPU in production profiling.
|
Pushed a fix for a bug in the original version of this PR: The fix guards the Note: |
SortedKeyValues sorts without populating lowerKeys, but Swap swapped lowerKeys unconditionally, panicking with index out of range for any header map without a Header-Order key (reachable from Header.Write, WriteSubset, and the http2 enumerateHeaders fallback). Guard the lowerKeys swap and reset order/lowerKeys on sorters reused from the pool, so a sorter previously used by SortedKeyValuesBy cannot leak a stale order (or stale lowered keys) into an orderless sort. Adds regression tests for both the panic and the pool-reuse case.
|
Closing to split this into focused PRs, since it bundled two unrelated changes:
Nothing here had been reviewed yet, so no review context is lost by the split. |
Summary
Replace stdlib
compress/gzipandcompress/flatewithgithub.com/klauspost/compress- drop-in replacement with a faster Huffman decoder. Measured ~5% total CPU reduction in production profiling of a high-throughput HTTP client workload.Eliminate per-comparison
strings.ToLowerinheaderSorter.Less- the sort comparator was callingstrings.ToLoweron every comparison, causing O(n log n) allocations per request. Pre-compute lowercase keys once before sorting and swap them in parallel with the key-value pairs.Use
lowerHeadermap lookup inencodeHeadersandencodeTrailers- replacestrings.ToLowerwith the existinglowerHeaderfunction inhttp2/headermap.go, which does a zero-allocation map lookup for the ~40 most common HTTP headers before falling back tostrings.ToLower.Combined measured savings: ~7% total CPU in production profiling under sustained high-throughput HTTP/2 traffic.
Changes
transport.go- swapcompress/gzipandcompress/flateimports togithub.com/klauspost/compressheader.go- addlowerKeysfield toheaderSorter, pre-compute inSortedKeyValuesBy, swap inSwap, use inLesshttp2/transport.go- replacestrings.ToLower(name)withlowerHeader(name)inencodeHeadersandencodeTrailers